/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.ui.rich.full.controller;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import br.ufmg.dees.insane.commons.controller.Controller;
import br.ufmg.dees.insane.commons.view.DrawingArea;
import br.ufmg.dees.insane.draw.CubicCurveDraw;
import br.ufmg.dees.insane.draw.Draw;
import br.ufmg.dees.insane.draw.LineDraw;
import br.ufmg.dees.insane.draw.LoftingRegionDraw;
import br.ufmg.dees.insane.draw.MapElementDraw;
import br.ufmg.dees.insane.draw.PointDraw;
import br.ufmg.dees.insane.draw.QuadCurveDraw;
import br.ufmg.dees.insane.draw.RegionDraw;
import br.ufmg.dees.insane.model.Model;
import br.ufmg.dees.insane.model.geo.GCubicCurve;
import br.ufmg.dees.insane.model.geo.GCurve;
import br.ufmg.dees.insane.model.geo.GLine;
import br.ufmg.dees.insane.model.geo.GPoint;
import br.ufmg.dees.insane.model.geo.GQuadCurve;
import br.ufmg.dees.insane.model.geo.GRegion;
import br.ufmg.dees.insane.model.geo.GeometricModel;
import br.ufmg.dees.insane.prep.MapElement;
import br.ufmg.dees.insane.prep.MapNode;
import br.ufmg.dees.insane.prep.MappingModel;
import br.ufmg.dees.insane.util.IPoint3d;

/**
 * @author Pitangueira, Roque & Brugiolo, Marco & Fonseca, Flavio
 * @version 0.0.1 Beta
 * @since jdk1.4.1
 */
public class MappingController implements Controller {
	
	private DrawingArea da;
	private Model model;
	private GeometricModel gmodel;
	private MappingModel mmodel;
	private int lastNode;
	private int lastElement;
	private ArrayList<Draw> ctrlPoints = new ArrayList<Draw>();
	private ArrayList<Draw> points = new ArrayList<Draw>();
	private ArrayList<Draw> lines = new ArrayList<Draw>();
	private ArrayList<Draw> quads = new ArrayList<Draw>();
	private ArrayList<Draw> cubics = new ArrayList<Draw>();
	private ArrayList<Draw> regions = new ArrayList<Draw>();
	private ArrayList<Object> selections = new ArrayList<Object>();
	private ArrayList<Draw> framelines = new ArrayList<Draw>();
	
//*****************************************************************************
	
    /**
     * The constructor method
     *
     * @param     parent   the parent of the class.
     * @see       gui.Interface
     */
    public MappingController(Model model, DrawingArea da) {
        this.model = model;
        this.da = da;
        this.gmodel = this.model.getGModel();
        this.mmodel = this.model.getMModel();
		this.lastNode = mmodel.getMapNodes().size();
		this.lastElement = mmodel.getMapElements().size();
    }
    
//*****************************************************************************
	
	public void draw(Graphics2D g) {
		
		double nodeScale = model.getState().nodeScale;
		g.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
		
		Draw draw;
		GPoint gp;
		GLine gl;
		GQuadCurve gq;
		GCubicCurve gc;
		GRegion gr;
		
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			g.setColor( model.getState().regionColor );
			draw.draw(g);
			if ( model.getState().getViewRegionLabel() && ((GRegion)draw.getRepresents()).getElements().isEmpty() ) {
				g.setColor( Color.black );
				//draw.drawLabel(g);
			}
		}
		
		g.setColor( model.getState().boundaryColor );
		rgs = regions.listIterator();
		while(rgs.hasNext()) {
			RegionDraw rDraw = (RegionDraw)rgs.next();
			rDraw.drawBoundary(g);
		}
		
		//Draw cubic curves
		ListIterator cbs = cubics.listIterator();
		while(cbs.hasNext()) {
			draw = (Draw)cbs.next();
			gc = (GCubicCurve)draw.getRepresents();
			int nParts = gc.getNParts();
			GPoint[] disc;
			disc = gc.getDiscrete();
			for (int count = 0; count <= nParts; count++) {
				
				if (nParts > 0 && count < nParts && gc.isFrame()) {
					g.setColor(model.getState().lineColor);
					draw = new LineDraw(da.getViewPoint(disc[count]), da.getViewPoint(disc[count+1]));
					draw.draw(g);
				}
				
				draw = new PointDraw(da.getViewPoint(disc[count]), disc[count]);
				draw.setScale(nodeScale*1.5);
				g.setColor( model.getState().markColor );
				draw.draw(g);
			}
		}
		
		//Draws quadratic curves
		ListIterator qcs = quads.listIterator();
		while(qcs.hasNext()) {
			draw = (Draw)qcs.next();
			gq = (GQuadCurve)draw.getRepresents();
			int nParts = gq.getNParts();
			GPoint[] disc;
			disc = gq.getDiscrete();
			for (int count = 0; count <= nParts; count++) {
				
				if (nParts > 0 && count < nParts && gq.isFrame()) {
					g.setColor(model.getState().lineColor);
					draw = new LineDraw(da.getViewPoint(disc[count]), da.getViewPoint(disc[count+1]));
					draw.draw(g);
				}
				
				draw = new PointDraw(da.getViewPoint(disc[count]), disc[count]);
				draw.setScale(nodeScale*1.5);
				g.setColor( model.getState().markColor );
				draw.draw(g);
			}
		}
		
		//Draws lines
		ListIterator lns = lines.listIterator();
		while(lns.hasNext()) {
			draw = (Draw)lns.next();
			gl = (GLine)draw.getRepresents();
			int nParts = gl.getNParts();
			GPoint[] disc;
			disc = gl.getDiscrete();
			for (int count = 0; count <= nParts; count++) {
				if (nParts > 0 && count < nParts && gl.isFrame()) {
					g.setColor(model.getState().lineColor);
					draw = new LineDraw(da.getViewPoint(disc[count]), da.getViewPoint(disc[count+1]));
					draw.draw(g);
				}
				
				draw = new PointDraw(da.getViewPoint(disc[count]), disc[count]);
				draw.setScale(nodeScale*1.5);
				g.setColor( model.getState().markColor );
				draw.draw(g);
			}
		}
		
		//Draws point
		ListIterator pts = points.listIterator();
		while(pts.hasNext()) {
			draw = (Draw)pts.next();
			gp = (GPoint)draw.getRepresents();
			GPoint[] disc = gp.getDiscrete();
			draw = new PointDraw(da.getViewPoint(disc[0]), disc[0]);
			draw.setScale(nodeScale*1.5);
			g.setColor( model.getState().markColor );
			draw.draw(g);
		}
		
		//Draws elements
		rgs = regions.listIterator();
        while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			gr = (GRegion)draw.getRepresents();
			ListIterator els = gr.getElements().listIterator();
			while(els.hasNext()) {
				MapElement el = (MapElement)els.next();
				draw = new MapElementDraw(da,el);
				g.setColor( model.getState().boundaryElementColor );
				((MapElementDraw)draw).drawBoundary(g);
				if ( model.getState().getViewElementLabel() ) {
					g.setColor( Color.black );
					//draw.drawLabel(g);
				}
				g.setColor( model.getState().pointColor );
				MapNode[] connec = el.getConnectivite();
				for (int count = 0; count < connec.length; count++) {
					MapNode mn = connec[count];
					gp = new GPoint(mn.getX(), mn.getY() );
					Point p1 = da.getViewPoint(gp.getP1());
					draw = new PointDraw(p1, gp);
					draw.setScale(3);
					draw.draw(g);
					if ( model.getState().getViewNodeLabel() ) {
						g.drawString( "" + mn.getLabel(), (int)p1.getX()+2, (int)p1.getY()-2 );
//						draw.setLabel("" + mn.getLabel());
//						draw.draw(g);
					}
				}
			}
		}
		
    }
    
//*****************************************************************************
	
    public void fit() {
        
        //Calculating bounds
        if (points.size() >= 2){
            Point leftUp = new Point();
            Point rightDown = new Point();
            PointDraw po;
            ListIterator pts = points.listIterator();
            po = (PointDraw)pts.next();
            leftUp.x = po.getLocation().x;
            leftUp.y = po.getLocation().y;
            rightDown.x = po.getLocation().x;
            rightDown.y = po.getLocation().y;
            while (pts.hasNext()){
                po = (PointDraw)pts.next();
                if (po.getLocation().x < leftUp.x){
                    leftUp.x = po.getLocation().x;
                }
                if (po.getLocation().x > rightDown.x){
                    rightDown.x = po.getLocation().x;
                }
                if (po.getLocation().y < leftUp.y){
                    leftUp.y = po.getLocation().y;
                }
                if (po.getLocation().y > rightDown.y){
                    rightDown.y = po.getLocation().y;
                }
            }
            pts = ctrlPoints.listIterator();
            while (pts.hasNext()){
                po = (PointDraw)pts.next();
                if (po.getLocation().x < leftUp.x){
                    leftUp.x = po.getLocation().x;
                }
                if (po.getLocation().x > rightDown.x){
                    rightDown.x = po.getLocation().x;
                }
                if (po.getLocation().y < leftUp.y){
                    leftUp.y = po.getLocation().y;
                }
                if (po.getLocation().y > rightDown.y){
                    rightDown.y = po.getLocation().y;
                }
            }
            
            //Adding some border
            leftUp.x = leftUp.x - 15;
            leftUp.y = leftUp.y - 15;
            rightDown.x = rightDown.x + 15;
            rightDown.y = rightDown.y + 15;
            
            Point2D p1 = da.getWorldPoint(leftUp);
            Point2D p2 = da.getWorldPoint(rightDown);
            Rectangle2D r = new Rectangle2D.Double(p1.getX(), p2.getY(), p2.getX(), p1.getY());
            
			da.setZoom(r);
        }
    }
	
//*****************************************************************************
	
	public void add(Object obj) {
		
		if (obj instanceof MapNode) {
			MapNode nd = (MapNode)obj;
			mmodel.add(nd);
		}
		
		if (obj instanceof MapElement) {
			MapElement el = (MapElement)obj;
			mmodel.add(el);
		}
		
	}
	
//*****************************************************************************
	
	public void removeCoincidentNodes() {
		
		List<MapNode> newNodes = new ArrayList<MapNode>();
		List oldNodes = mmodel.getMapNodes();
		ListIterator onds = oldNodes.listIterator();
		while(onds.hasNext()) {
			MapNode ond = (MapNode)onds.next();
			boolean insert = true;
			ListIterator nnds = newNodes.listIterator();
			while(nnds.hasNext()) {
				MapNode nnd = (MapNode)nnds.next();
				if ( (nnd.distance(ond))<0.05 ) {
					ond.setLabel(nnd.getLabel());
					insert = false;
					break;
				}
			}
			if ( insert ) {
				newNodes.add(ond);
			}
		}
		
		int label = 0;
		List gRegions = gmodel.getRegions();
		ListIterator rgs = (gRegions).listIterator();
		while(rgs.hasNext()) {
			GRegion rg = (GRegion)rgs.next();
			ListIterator els = (rg.getElements()).listIterator();
			while(els.hasNext()) {
				MapElement el = (MapElement)els.next();
				MapNode[] conec = el.getConnectivite();
				for (int count = 0; count < conec.length; count++) {
					label = conec[count].getLabel();
					ListIterator nnds = newNodes.listIterator();
					while(nnds.hasNext()) {
						MapNode nnd = (MapNode)nnds.next();
						if ( label==nnd.getLabel() ) {
							conec[count] = nnd;
							break;
						}
					}
				}
				MapNode[] bound = el.getBoundary();
				for (int count = 0; count < bound.length; count++) {
					label = bound[count].getLabel();
					ListIterator nnds = newNodes.listIterator();
					while(nnds.hasNext()) {
						MapNode nnd = (MapNode)nnds.next();
						if ( label==nnd.getLabel() ) {
							bound[count] = nnd;
							break;
						}
					}
				}
			}
		}
		
		label = 0;
		onds = newNodes.listIterator();
		while(onds.hasNext()) {
			MapNode ond = (MapNode)onds.next();
			ond.setLabel(++label);
		}
		
		mmodel.setMapNodes(newNodes);
	} // end of removeCoincidentNodes
	
//*****************************************************************************
	
	public void setNumber() {
//		mmodel.getMapNodes();
//		mmodel.getMapElements();
		int label = 0;
		ListIterator els = ( mmodel.getMapElements() ).listIterator();
		while(els.hasNext()) {
			MapElement el = (MapElement)els.next();
			label++;
			el.setLabel(label);
		}
	}
	
//*****************************************************************************
	
	public boolean contains(Point2D po) {
		return false;
	}
	
//*****************************************************************************
	
    public boolean contains(Line2D l) {
        return false;
    }
	
//*****************************************************************************
	
	public boolean contains(GCurve gc) {
		return false;
	}
	
//*****************************************************************************
	
	public Point pick(Point p) {
		Draw draw;
		
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			if (draw.contains(p)) {
				return draw.getLocation();
			}
		}
		
		return p;
	}
    
//*****************************************************************************
	
    public void remove(Object obj) {
    }
    
//*****************************************************************************
	
    public List removeSelection() {
        return null;
    }
	
//*****************************************************************************
	
	public void select(Point p) {
		Draw draw;
		int index;
		
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			if (draw.contains(p)){
				if (draw.isSelected()) {
					draw.setSelected(false);
					index = selections.indexOf(draw.getRepresents());
					selections.remove(index);
					return;
				}else{
					draw.setSelected(true);
					selections.add(draw.getRepresents());
					return;
				}
			}
		}
	}
	
//*****************************************************************************
	
	public void selectAll() {
		Draw draw;
		selections.clear();
		
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			draw.setSelected(true);
			selections.add(draw.getRepresents());
		}
	}
	
//*****************************************************************************
	
	public void unSelectAll() {
		Draw draw;
		selections.clear();
		
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			draw.setSelected(false);
		}
	}
	
//*****************************************************************************
	
	public void removeAll() {
		mmodel.clearMapNodes();
		mmodel.clearMapElements();
		lastNode = 0;
		lastElement = 0;
	}
	
//*****************************************************************************
	
	public ArrayList getSelection() {
		ListIterator rgs = selections.listIterator();
		while(rgs.hasNext()) {
			GRegion gr = (GRegion)rgs.next();
			ListIterator els = ( gr.getElements() ).listIterator();
			while(els.hasNext()) {
				MapElement el = (MapElement)els.next();
				mmodel.remove(el);
			}
			gr.clearElements();
		}
		compose();
		return selections;
	}
	
//*****************************************************************************
	
	public void compose() {
		ctrlPoints.clear();
		points.clear();
		lines.clear();
		quads.clear();
		cubics.clear();
		regions.clear();
		framelines.clear();
		
        Point2D po;
        Point p;
		ListIterator pts = gmodel.getControlPoints().listIterator();
		while (pts.hasNext()) {
			po = (Point2D)pts.next();
			p = da.getViewPoint(po);
			ctrlPoints.add(new PointDraw(p, po));
		}
		
        pts = gmodel.getPoints().listIterator();
        while (pts.hasNext()) {
            po = (Point2D)pts.next();
            p = da.getViewPoint(po);
            points.add(new PointDraw(p, po));
        }
		
		Line2D li;
        Point p1;
        Point p2;
        ListIterator lns = gmodel.getLines().listIterator();
        while (lns.hasNext()) {
            li = (Line2D)lns.next();
            p1 = da.getViewPoint(li.getP1());
            p2 = da.getViewPoint(li.getP2());
            lines.add(new LineDraw(p1, p2, li));
        }
		
		QuadCurve2D qu;
		Point ctrl1, ctrl2;
		ListIterator qds = gmodel.getQuads().listIterator();
		while (qds.hasNext()) {
			qu = (QuadCurve2D)qds.next();
			p1 = da.getViewPoint(qu.getP1());
			ctrl1 = da.getViewPoint(qu.getCtrlPt());
			p2 = da.getViewPoint(qu.getP2());
			quads.add(new QuadCurveDraw(p1, ctrl1, p2, qu));
		}
		
		CubicCurve2D cb;
		ListIterator cbs = gmodel.getCubics().listIterator();
		while (cbs.hasNext()) {
			cb = (CubicCurve2D)cbs.next();
			p1 = da.getViewPoint(cb.getP1());
			ctrl1 = da.getViewPoint(cb.getCtrlP1());
			ctrl2 = da.getViewPoint(cb.getCtrlP2());
			p2 = da.getViewPoint(cb.getP2());
			cubics.add(new CubicCurveDraw(p1, ctrl1, ctrl2, p2, cb));
		}
		
		ListIterator rgs = gmodel.getRegions().listIterator();
		while (rgs.hasNext()) {
			GRegion rg = (GRegion)rgs.next();
			if (rg.getCurves().size()==2)
				regions.add(new LoftingRegionDraw(da, rg));
			else
				regions.add(new RegionDraw(da, rg));
		}
		
	}
    
//*****************************************************************************
	
    public Object get(Point2D po) {
		Point location = da.getViewPoint(po);
        Draw draw;
        
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			if (draw.contains(location)){
				return draw.getRepresents();
			}
		}
        return null;
    }
    
//*****************************************************************************
	
	public boolean isSelected() {
		Draw draw;
		
		ListIterator rgs = regions.listIterator();
		while(rgs.hasNext()) {
			draw = (Draw)rgs.next();
			if (draw.isSelected()){
				return true;
			}
		}
		return false;
	}
	
//*****************************************************************************
	
	public ArrayList getRegions() {
		return (ArrayList)gmodel.getRegions();
	}
	
//*****************************************************************************
	
	public int getLastNode() {
		return lastNode;
	}
	
//*****************************************************************************
	
	public void setLastNode(int lb) {
		lastNode = lb;
	}
	
//*****************************************************************************
	
	public int getLastElement() {
		return lastElement;
	}
	
//*****************************************************************************
	
	public void setLastElement(int lb) {
		lastElement = lb;
	}
	
//*****************************************************************************
	
	public void setColor() {
	}

	public boolean contains(IPoint3d po) {
		// TODO Auto-generated method stub
		return false;
	}
	
//*****************************************************************************
}
